package org.gitman.agentplatform.ui.simulation;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;

import org.gitman.agentplatform.agent.api.Agent;
import org.gitman.agentplatform.agent.api.AgentState;
import org.gitman.agentplatform.agent.api.InternalAgentInfo;
import org.gitman.agentplatform.core.AgentManager;
import org.gitman.agentplatform.core.MultiRunParameters;
import org.gitman.agentplatform.core.PlatformManager;
import org.gitman.agentplatform.ui.MainFrame;
import org.gitman.agentplatform.ui.actions.ActionsManager;
import org.gitman.agentplatform.ui.api.View;
import org.gitman.agentplatform.ui.api.View.MessageType;
import org.gitman.agentplatform.ui.utils.ColorRenderer;

public class SimulationPanel extends JPanel {
	private JLabel iterationLabel;
	private ActionsManager actionsManager;
	private AgentTableModel model;
	private JTextField aRelie;
	private JCheckBox isRelieKnow;
	private JTextField relieField;
	private RangedTextField observField;
	private RangedTextField predictField;
	private RangedTextField aObserv;	
	private RangedTextField aPredict;
	private JTextField aStart;
	private JTextField aEnd;
	private JTextField args;
	private JPanel detailsPanel;
	private JComboBox combo;
	private Random rand;
	private AgentTable table;
	private InternalAgentInfo info;
	private Integer prevID;
	private JCheckBox observCheckBox;
	private JCheckBox relieCheckBox;
	private JCheckBox predictCheckBox;
	private JCheckBox observMultiBox;
	private JTextField observMulitTextField;	
	private JCheckBox predictMultiBox;
	private JTextField predictMulitTextField;	
	private JCheckBox relieMultiBox;
	private JTextField relieMulitTextField;
	private JCheckBox randomOrderBox;
	private JTextField randomOrderTextField;


	public SimulationPanel(final MainFrame mainFrame) {
		info = null;
		rand = new Random(Calendar.getInstance().getTimeInMillis());
		actionsManager = mainFrame.getActionsManager();
		AgentManager agentManager = PlatformManager.getInstatnce().getSimulation().getAgentManager();
		setLayout(new BorderLayout());
		JPanel upperPanel = new JPanel(new BorderLayout());
		JPanel controlPanel = new JPanel(new BorderLayout());
		upperPanel.add(controlPanel, BorderLayout.CENTER);
		add(upperPanel, BorderLayout.NORTH);
		JPanel iterPanel = new JPanel(new BorderLayout());
		iterPanel.add(new JLabel("Iteration:"), BorderLayout.WEST);
		iterationLabel = new JLabel();
		setIteration(0);
		iterPanel.add(iterationLabel, BorderLayout.EAST);
		controlPanel.add(iterPanel, BorderLayout.SOUTH);
		JPanel buttoPanel = new JPanel(new GridLayout(1,6));
		controlPanel.add(buttoPanel);
		buttoPanel.add(new JButton(actionsManager.rewindAction));
		buttoPanel.add(new JButton(actionsManager.pauseAction));
		buttoPanel.add(new JButton(actionsManager.stepAction));
		buttoPanel.add(new JButton(actionsManager.playAction));
		buttoPanel.add(new JButton(actionsManager.multiRunAction));
		buttoPanel.add(new JButton(actionsManager.fForwardAction));
		
		controlPanel.setBorder(BorderFactory.createTitledBorder("Simulation control"));
		
		JPanel multirunPanel = new JPanel(new GridLayout(5,2));
		upperPanel.add(multirunPanel, BorderLayout.SOUTH);
		multirunPanel.setBorder(BorderFactory.createTitledBorder("Multirun configuration"));
		multirunPanel.add(new JLabel("Use the pattern for multirun:"));
		multirunPanel.add(new JLabel("min;step;max"));		
		observMultiBox = new JCheckBox("Observability");
		observMulitTextField = new JTextField();
		observMulitTextField.setEnabled(false);
		observMultiBox.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				if(selected){
					observMulitTextField.setEnabled(true);
				}else{
					observMulitTextField.setEnabled(false);
				}
			}
		});
		multirunPanel.add(observMultiBox);
		multirunPanel.add(observMulitTextField);			
		predictMultiBox = new JCheckBox("Predictability");
		predictMulitTextField = new JTextField();
		predictMulitTextField.setEnabled(false);
		predictMultiBox.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				if(selected){
					predictMulitTextField.setEnabled(true);
				}else{
					predictMulitTextField.setEnabled(false);
				}
			}
		});
		multirunPanel.add(predictMultiBox);
		multirunPanel.add(predictMulitTextField);		
		relieMultiBox = new JCheckBox("Reliability");
		relieMulitTextField = new JTextField();
		relieMulitTextField.setEnabled(false);
		relieMultiBox.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				if(selected){
					relieMulitTextField.setEnabled(true);
				}else{
					relieMulitTextField.setEnabled(false);
				}
			}
		});
		multirunPanel.add(relieMultiBox);
		multirunPanel.add(relieMulitTextField);
		randomOrderBox = new JCheckBox("Randomize agent order");
		randomOrderTextField = new JTextField();
		randomOrderTextField.setEnabled(false);
		randomOrderBox.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				if(selected){
					randomOrderTextField.setEnabled(true);
				}else{
					randomOrderTextField.setEnabled(false);
				}
			}
		});
		multirunPanel.add(randomOrderBox);
		multirunPanel.add(randomOrderTextField);
		
		JPanel agentPanel = new JPanel(new BorderLayout());
		add(agentPanel, BorderLayout.CENTER);
		agentPanel.setBorder(BorderFactory.createTitledBorder("Agents"));
		model = new AgentTableModel();
		table = new AgentTable(model);
		JScrollPane scrollPane = new JScrollPane(table);
		agentPanel.add(scrollPane);
		table.getColumnModel().getColumn(0).setPreferredWidth(50);
		table.getColumnModel().getColumn(1).setPreferredWidth(30);
		table.getColumnModel().getColumn(2).setPreferredWidth(340);
		table.getColumnModel().getColumn(3).setPreferredWidth(80);
		table.getColumnModel().getColumn(4).setPreferredWidth(100);
		ColorRenderer colorRenderer = new ColorRenderer();
		table.setDefaultRenderer(Color.class, colorRenderer);
		JPanel optionAgentPanel = new JPanel(new BorderLayout());
		agentPanel.add(optionAgentPanel, BorderLayout.SOUTH);
		JPanel eastoptionsPanel = new JPanel(new FlowLayout());
		optionAgentPanel.add(eastoptionsPanel, BorderLayout.EAST);
		eastoptionsPanel.add(new JButton(actionsManager.addAgentAction));
		eastoptionsPanel.add(new JButton(actionsManager.editAgentAction));
		eastoptionsPanel.add(new JButton(actionsManager.removeAgentAction));
		
		
		JPanel modifyAgentPanel = new JPanel(new BorderLayout());
		add(modifyAgentPanel, BorderLayout.SOUTH);
		JPanel genralParams= new JPanel(new GridLayout(3,1));
		genralParams.setBorder(BorderFactory.createTitledBorder("Agent's general parameters"));
		modifyAgentPanel.add(genralParams, BorderLayout.SOUTH);
		JPanel observP = new JPanel(new BorderLayout()); 
		genralParams.add(observP);
		observCheckBox = new JCheckBox("Observability (visible vertices around)");
		observCheckBox.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				PlatformManager platformManager = PlatformManager.getInstatnce();
				AgentManager agentManager = platformManager.getSimulation().getAgentManager();
				agentManager.setUseDefObserv(selected);
				if(selected){
					try{
						observField.parseText();
						if(observField.getMin() < 0 || observField.getMin() > observField.getMax()){
							throw new IllegalArgumentException();
						}	
						agentManager.setDefaultMinObserv(observField.getMin());
						agentManager.setDefaultMaxObserv(observField.getMax());
					}catch(Exception ex1){
						View.showMessage("Wrong format of observability parameter.", MessageType.ERROR);
						box.setSelected(false);
					}
				}				
			}
		});
		observP.add(observCheckBox);
		observField = new RangedTextField(agentManager.getDefaultMinObserv(), agentManager.getDefaultMaxPredict(), 6);
		observField.getDocument().addDocumentListener(new Field2BoxListener(observCheckBox));
		observP.add(observField, BorderLayout.EAST);		
		JPanel predictP = new JPanel(new BorderLayout()); 
		genralParams.add(predictP);
		predictCheckBox = new JCheckBox("Predictability (number of planned steps)");
		predictCheckBox.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				PlatformManager platformManager = PlatformManager.getInstatnce();
				AgentManager agentManager = platformManager.getSimulation().getAgentManager();
				agentManager.setUseDefPredict(selected);
				if(selected){
					try{
						predictField.parseText();
						if(predictField.getMin() < 0 || predictField.getMin() > predictField.getMax()){
							throw new IllegalArgumentException();
						}
						agentManager.setDefaultMinPredict(predictField.getMin());
						agentManager.setDefaultMaxPredict(predictField.getMax());
					}catch(Exception ex1){
						View.showMessage("Wrong format of predictability parameter.", MessageType.ERROR);
						box.setSelected(false);
					}
				}	
				
			}
		});
		predictP.add(predictCheckBox);
		predictField = new RangedTextField(agentManager.getDefaultMinPredict(), agentManager.getDefaultMaxPredict(), 6);
		predictField.getDocument().addDocumentListener(new Field2BoxListener(predictCheckBox));
		predictP.add(predictField, BorderLayout.EAST);
		JPanel relieP = new JPanel(new BorderLayout()); 
		genralParams.add(relieP);	
		relieCheckBox = new JCheckBox("Reliability (value[0.0-1.0])");
		relieCheckBox.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				JCheckBox box = (JCheckBox)e.getSource();
				boolean selected = box.isSelected();
				PlatformManager platformManager = PlatformManager.getInstatnce();
				AgentManager agentManager = platformManager.getSimulation().getAgentManager();
				agentManager.setUseDefReliab(selected);
				if(selected){
					try{
						Double val = Double.parseDouble(relieField.getText());
						if(val < 0.0 || val > 1.0){
							throw new IllegalArgumentException();
						}
						agentManager.setDefaultReliab(Double.parseDouble(relieField.getText()));
					}catch(Exception ex1){
						View.showMessage("Wrong format of reliability parameter.", MessageType.ERROR);
						box.setSelected(false);
					}
				}	
			}
		});
		relieP.add(relieCheckBox);
		relieField = new JTextField(agentManager.getDefaultReliab() + "", 6);
		relieField.getDocument().addDocumentListener(new Field2BoxListener(relieCheckBox));
		relieP.add(relieField, BorderLayout.EAST);
		detailsPanel = new JPanel();
		modifyAgentPanel.add(detailsPanel, BorderLayout.NORTH);
	}		
	public void setIteration(int iteration){
		iterationLabel.setText(iteration + "     ");
	}	
	public void showDetailsAgentPanel(){
		detailsPanel.removeAll();
		detailsPanel.setLayout(new BorderLayout());	
		detailsPanel.setBorder(BorderFactory.createTitledBorder("Agent Details"));		
		JPanel selectClassPanel = new JPanel(new GridLayout(1,2));
		detailsPanel.add(selectClassPanel, BorderLayout.NORTH);
		selectClassPanel.add(new JLabel("Select Agents class"));
		combo = new JComboBox();		
		Map<Class<? extends Agent>, String> availableAgentsInfo = PlatformManager.getInstatnce().getAvailableAgentsInfo();
		for(Class<? extends Agent> clazz : availableAgentsInfo.keySet()){
			combo.addItem(clazz.getSimpleName());		
		}
		combo.setSelectedItem(null);
		selectClassPanel.add(combo);		
		final JTextPane descr = new JTextPane();		
		descr.setEditable(false);
		descr.setPreferredSize(new Dimension(100, 60));
		JScrollPane scroll = new JScrollPane(descr);
		scroll.setBorder(BorderFactory.createTitledBorder("Description"));
		detailsPanel.add(scroll, BorderLayout.CENTER);
		combo.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				JComboBox cb = (JComboBox)e.getSource();
				Class<? extends Agent> clazz = null;
				try {
					clazz = PlatformManager.getInstatnce().getClassBySimpleName(((String)cb.getSelectedItem()));
					descr.setText(getDescriptionByClass(clazz));
				} catch (Exception e1) {
					View.showMessage(e1.getMessage(), MessageType.ERROR);
					e1.printStackTrace();
				}
				
			}
		});

		AgentManager agentManager = PlatformManager.getInstatnce().getSimulation().getAgentManager();
		JPanel others = new JPanel(new BorderLayout());
		detailsPanel.add(others, BorderLayout.SOUTH);
		JPanel params = new JPanel(new GridLayout(3,4));
		others.add(params, BorderLayout.NORTH);
		params.add(new JLabel("Observability"));
		aObserv = new RangedTextField(agentManager.getDefaultMinObserv(), agentManager.getDefaultMaxObserv(), 6);
		params.add(aObserv);
		params.add(new JLabel("   Starting ID"));
		aStart = new JTextField();
		params.add(aStart);
		params.add(new JLabel("Predictability"));
		aPredict = new RangedTextField(agentManager.getDefaultMinPredict(), agentManager.getDefaultMaxPredict(), 6);
		params.add(aPredict);
		params.add(new JLabel("   Target ID"));
		aEnd = new JTextField();
		params.add(aEnd);		
		params.add(new JLabel("Reliability"));
		aRelie = new JTextField(agentManager.getDefaultReliab() +"");
		params.add(aRelie);
		isRelieKnow = new JCheckBox("known?");
		params.add(isRelieKnow);	
		params.add(new JLabel(""));	
		
		JPanel argsPanel = new JPanel(new BorderLayout());
		others.add(argsPanel, BorderLayout.CENTER);
		argsPanel.add(new JLabel("Agent args.:   "), BorderLayout.WEST);		
		args = new JTextField();
		argsPanel.add(args, BorderLayout.CENTER);
		
		
		JPanel optionPanel = new JPanel(new BorderLayout());
		others.add(optionPanel, BorderLayout.SOUTH);
		JPanel flow = new JPanel(new FlowLayout());
		optionPanel.add(flow, BorderLayout.EAST);
		flow.add(new JButton(actionsManager.okAddingAgentAction));
		flow.add(new JButton(actionsManager.cancelAddingAgentAction));
		
	}
	public void hideDetailsPanel() {
		detailsPanel.removeAll();
		detailsPanel.setBorder(null);
		revalidate();
	}
	public Class<? extends Agent> getAgentClass() throws Exception {
		return PlatformManager.getInstatnce().getClassBySimpleName(((String)combo.getSelectedItem()));
	}
	public InternalAgentInfo getAgentInfo() throws NumberFormatException{
		InternalAgentInfo agentInfo = null;
		if(isAgentEdited()){
			agentInfo = info;
			agentInfo.setAgentState(AgentState.changed);
		}else{
			agentInfo = new InternalAgentInfo();
			agentInfo.setColor(getNextRandomColor());
			agentInfo.setAgentState(AgentState.fresh);
		}
		agentInfo.setStartVertexID(Integer.parseInt(aStart.getText()));
		agentInfo.setEndVertexID(Integer.parseInt(aEnd.getText()));
		agentInfo.setRelie(Double.parseDouble(aRelie.getText()));
		agentInfo.setRelieKnown(isRelieKnow.isSelected());
		aPredict.parseText();
		aObserv.parseText();
		agentInfo.setPredict(aPredict.getMin(), aPredict.getMax());
		agentInfo.setObserv(aObserv.getMin(), aObserv.getMax());
		agentInfo.setArgs(args.getText());		
		return agentInfo;
	}
	public String getArgs() {
		return args.getText();
	}
	public void fireTableChanged() {
		model.fireTableDataChanged();
	}
	public JTable getTable(){
		return table;
	}
	public void loadAgent2Edit(InternalAgentInfo info, String className) {
		this.info = info;
		prevID = info.getCurrentVertexID() != null? info.getCurrentVertexID() : info.getStartVertexID();
		combo.setSelectedItem(className);
		
		aStart.setText(info.getStartVertexID().toString());
		aEnd.setText(info.getEndVertexID().toString());
		aRelie.setText(info.getInternalRelie().toString());
		aPredict.setRange(info.getMinPredict(), info.getMaxPredict());
		aObserv.setRange(info.getMinObserv(), info.getMaxObserv());
		args.setText(info.getArgs());
				
	}
	public void unloadAgent2Edit(){
		info = null;
	}
	public boolean isAgentEdited(){
		return info != null;
	}
	public Integer getPrevID(){
		return prevID;
	}
	public void cleanDefaultAgentParams() {
		AgentManager agentManager = PlatformManager.getInstatnce().getSimulation().getAgentManager();
								
		relieField.setText(agentManager.getDefaultReliab() + "");
		observField.setRange(agentManager.getDefaultMinObserv(), agentManager.getDefaultMaxPredict());
		predictField.setRange(agentManager.getDefaultMinPredict(), agentManager.getDefaultMaxPredict());
				
		observCheckBox.setSelected(false);
		predictCheckBox.setSelected(false);
		relieCheckBox.setSelected(false);
	}
	public void loadDefaultAgentParams(){
		AgentManager agentManager = PlatformManager.getInstatnce().getSimulation().getAgentManager();

		observField.setRange(agentManager.getDefaultMinObserv(), agentManager.getDefaultMaxPredict());
		predictField.setRange(agentManager.getDefaultMinPredict(), agentManager.getDefaultMaxPredict());		
		
		observCheckBox.setSelected(agentManager.isUseDefObserv());
		predictCheckBox.setSelected(agentManager.isUseDefPredict());
		relieCheckBox.setSelected(agentManager.isUseDefReliab());
	}
	public MultiRunParameters getMultiRunParams() throws Exception{
		MultiRunParameters multiRunParameters = new MultiRunParameters();
		if(observMultiBox.isSelected()){
			multiRunParameters.setChangeObserv(true);
			String[] parts = observMulitTextField.getText().split(";");
			multiRunParameters.setMinObserv(Integer.parseInt(parts[0]));
			multiRunParameters.setStepObserv(Integer.parseInt(parts[1]));
			multiRunParameters.setMaxObserv(Integer.parseInt(parts[2]));
		}
		if(predictMultiBox.isSelected()){
			multiRunParameters.setChangePredict(true);
			String[] parts = predictMulitTextField.getText().split(";");
			multiRunParameters.setMinPredict(Integer.parseInt(parts[0]));
			multiRunParameters.setStepPredict(Integer.parseInt(parts[1]));
			multiRunParameters.setMaxPredict(Integer.parseInt(parts[2]));
		}
		if(relieMultiBox.isSelected()){
			multiRunParameters.setChangeRelie(true);
			String[] parts = relieMulitTextField.getText().split(";");
			multiRunParameters.setMinRelie(Double.parseDouble(parts[0]));
			multiRunParameters.setStepRelie(Double.parseDouble(parts[1]));
			multiRunParameters.setMaxRelie(Double.parseDouble(parts[2]));
		}	
		if(randomOrderBox.isSelected()){
			multiRunParameters.setRandOrder(true);
			multiRunParameters.setRandOrderNr(Integer.parseInt(randomOrderTextField.getText()));
		}		
		return multiRunParameters;
	}

	private String getDescriptionByClass(Class<? extends Agent> clazz){
		return PlatformManager.getInstatnce().getAvailableAgentsInfo().get(clazz);
	}
	private Color getNextRandomColor(){
		Color color = null;
		Set<Color> set = new HashSet<Color>();
		for(InternalAgentInfo info : PlatformManager.getInstatnce().getSimulation().getAgentManager().getAllAgentsInfo()){
			set.add(info.getColor());
		}
		do{
			color = new Color(rand.nextInt(256), rand.nextInt(256), rand.nextInt(256));
		}while(set.contains(color));
		return color;
	}
	
	
	private class Field2BoxListener implements DocumentListener{
		private JCheckBox box;
		
		public Field2BoxListener(JCheckBox box){
			this.box = box;
		}		
		
		@Override
		public void insertUpdate(DocumentEvent e) {
			box.setSelected(false);			
		}

		@Override
		public void removeUpdate(DocumentEvent e) {
			box.setSelected(false);	
		}

		@Override
		public void changedUpdate(DocumentEvent e) {
			box.setSelected(false);	
		}
		
	}
}
